﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using Centido.Core.Dto;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Centido.Core.Test
{
    /// <summary>
    ///   Testing the data transfer object for a table.
    /// </summary>
    [TestClass]
    public class TableDtoTest : GenericDtoTest
    {
        /// <summary>
        ///   The current domain name.
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   The current domain.
        /// </summary>
        private Domain _domain;

        /// <summary>
        ///   The table.
        /// </summary>
        private Table _table;

        /// <summary>
        /// Test initialiaze
        /// </summary>
        [TestInitialize]
        public void Initialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));

            _domain.DeleteAll();

            _table = _domain.AddTable(StringProperty.RandomIdentifier());
        }

        
        /// <summary>
        /// Test clean up
        /// </summary>
        [TestCleanup]
        public void CleanUp()
        {
            _domain.DeleteAll();
        }


        /// <summary>
        ///   Retrieves the name of the data transfer object xml tag.
        /// </summary>
        /// <returns></returns>
        protected override string GetXmlTag()
        {
            return TableDto.XmlTag;
        }


        /// <summary>
        ///   Retrives the current xml schema version.
        /// </summary>
        /// <returns></returns>
        protected override string GetCurrentXmlSchemaVersion()
        {
            return TableDto.CurrentXmlSchemaVersion;
        }


        /// <summary>
        ///   Creates a new data transfer object.
        /// </summary>
        /// <returns></returns>
        protected override DataTransferObject CreateDto()
        {
            return new TableDto
            {
                Name = "table_name",
                DisplayName = "The display name",
                Description = "The table description",
                HasUnitLevelSecurity = false
            };
        }


        /// <summary>
        ///   Retrieves the list of data transfer object fields.
        /// </summary>
        /// <returns></returns>
        protected override IEnumerable<DtoFieldInfo> GetFields()
        {
            yield return new DtoFieldInfo
            {
                Name = "Name",
                XmlName = TableDto.NameAttribute,
                IsRequired = true,
                FieldType = DtoFieldType.String,
                SampleValue = "table_name_new"
            };

            yield return new DtoFieldInfo
            {
                Name = "DisplayName",
                XmlName = TableDto.DisplayNameAttribute,
                IsRequired = false,
                FieldType = DtoFieldType.String,
                SampleValue = "The display name"
            };

            yield return new DtoFieldInfo
            {
                Name = "Description",
                XmlName = TableDto.DescriptionAttribute,
                IsRequired = false,
                FieldType = DtoFieldType.String,
                SampleValue = "The description"
            };

            yield return new DtoFieldInfo
            {
                Name = "HasUnitLevelSecurity",
                XmlName = TableDto.HasUnitLevelSecurityAttribute,
                IsRequired = false,
                FieldType = DtoFieldType.Boolean,
                SampleValue = false
            };
        }


        /// <summary>
        ///   Copies the properties of the data transfer object to the corresponding object.
        /// </summary>
        /// <param name="dto">The data transfer object.</param>
        /// <returns>Returns the corresponding object after copying.</returns>
        protected override object CopyFrom(DataTransferObject dto)
        {
            ((TableDto)dto).CopyTo(_table);

            return _table;
        }


        /// <summary>
        ///   Creates a data transfer object from the correpsonding object.
        /// </summary>
        /// <param name="dto">The created data transfer object.</param>
        /// <param name="initialObject">The initial object.</param>
        protected override void CreateDtoFromObject(out DataTransferObject dto, out object initialObject)
        {
            dto = TableDto.CreateFrom(_table);
            initialObject = _table;
        }


        /// <summary>
        ///   Reading the column list from xml.
        /// </summary>
        [TestMethod]
        public void ReadingColumnListFromXml()
        {
            // Creating the table
            var xml = string.Format("<{0} {1}=\"{2}\">",
                TableDto.XmlTag,
                TableDto.NameAttribute,
                StringProperty.RandomIdentifier());

            // Creating the column list
            xml += string.Format("<{0}><{1} {2}=\"{3}\" /><{4} {5}=\"{6}\"></{7}></{8}></{9}>",
                TableDto.ColumnListTag,
                IntColumnDto.XmlTag,
                ColumnDto.NameAttribute,
                StringProperty.RandomIdentifier(),
                GuidColumnDto.XmlTag,
                ColumnDto.NameAttribute,
                StringProperty.RandomIdentifier(),
                GuidColumnDto.XmlTag,
                TableDto.ColumnListTag,
                TableDto.XmlTag);

            var stream = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

            var tableDto = new TableDto();

            tableDto.ReadXml(stream);

            Assert.AreEqual(tableDto.Columns.Count(), 2);
            Assert.AreEqual(tableDto.Columns.Count(c => c is IntColumnDto), 1);
            Assert.AreEqual(tableDto.Columns.Count(c => c is GuidColumnDto), 1);
        }


        /// <summary>
        ///   Reading an empty column list from xml.
        /// </summary>
        [TestMethod]
        public void ReadingEmptyColumnListFromXml()
        {
            var xml = string.Format("<{0} {1}=\"{2}\">",
                TableDto.XmlTag,
                TableDto.NameAttribute,
                "my_table");

            // Creating the empty column list
            xml += string.Format("<{0} /></{1}>",
                TableDto.ColumnListTag,
                TableDto.XmlTag);

            var stream = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

            var refColumnDto = new TableDto();

            refColumnDto.ReadXml(stream);

            Assert.AreEqual(refColumnDto.Columns.Count(), 0);
        }


        /// <summary>
        ///   Writing and reading the column list.
        /// </summary>
        [TestMethod]
        public void WritingAndReadingColumnList()
        {
            var tableDto = new TableDto
            {
                Name = "my_table"
            };

            tableDto.AddColumn(new GuidColumnDto
                {
                    Name = "guid_column"
                });
            tableDto.AddColumn(new IntColumnDto
                {
                    Name = "int_column",
                    Maximum = 10
                });

            var memoryStream = new MemoryStream();
            var writer = new XmlTextWriter(memoryStream, Encoding.UTF8);

            tableDto.WriteXml(writer);

            writer.Flush();

            memoryStream.Position = 0;
            var streamReader = new StreamReader(memoryStream);
            var xml = streamReader.ReadToEnd();

            Assert.IsFalse(string.IsNullOrEmpty(xml));

            memoryStream.Position = 0;
            var reader = new XmlTextReader(memoryStream);

            var readIndexDto = new TableDto();

            readIndexDto.ReadXml(reader);

            Assert.AreEqual(readIndexDto.Columns.Count(), 2);
        }


        /// <summary>
        ///   Reading the index list from xml.
        /// </summary>
        [TestMethod]
        public void ReadingIndexListFromXml()
        {
            // Creating the table
            var xml = string.Format("<{0} {1}=\"{2}\">",
                TableDto.XmlTag,
                TableDto.NameAttribute,
                StringProperty.RandomIdentifier());

            // Creating the index list
            xml += string.Format("<{0}><{1} /><{2}></{3}></{4}></{5}>",
                TableDto.IndexListTag,
                IndexDto.XmlTag,
                IndexDto.XmlTag,
                IndexDto.XmlTag,
                TableDto.IndexListTag,
                TableDto.XmlTag);

            var stream = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

            var tableDto = new TableDto();

            tableDto.ReadXml(stream);

            Assert.AreEqual(tableDto.Indexes.Count(), 2);
        }


        /// <summary>
        ///   Reading an empty index list from xml.
        /// </summary>
        [TestMethod]
        public void ReadingEmptyIndexListFromXml()
        {
            var xml = string.Format("<{0} {1}=\"{2}\">",
                TableDto.XmlTag,
                TableDto.NameAttribute,
                "my_table");

            // Creating an empty index list
            xml += string.Format("<{0} /></{1}>",
                TableDto.IndexListTag,
                TableDto.XmlTag);

            var stream = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

            var refColumnDto = new TableDto();

            refColumnDto.ReadXml(stream);

            Assert.AreEqual(refColumnDto.Indexes.Count(), 0);
        }


        /// <summary>
        ///   Writing and reading index list.
        /// </summary>
        [TestMethod]
        public void WritingAndReadingIndexList()
        {
            var tableDto = new TableDto
            {
                Name = "my_table"
            };

            tableDto.AddIndex(new IndexDto
            {
                IsUnique = true
            });

            var indexDto = new IndexDto();

            indexDto.AddColumn("a");
            indexDto.AddColumn("b");

            tableDto.AddIndex(indexDto);

            var memoryStream = new MemoryStream();
            var writer = new XmlTextWriter(memoryStream, Encoding.UTF8);

            tableDto.WriteXml(writer);

            writer.Flush();

            memoryStream.Position = 0;
            var streamReader = new StreamReader(memoryStream);
            var xml = streamReader.ReadToEnd();

            Assert.IsFalse(string.IsNullOrEmpty(xml));

            memoryStream.Position = 0;
            var reader = new XmlTextReader(memoryStream);

            var readIndexDto = new TableDto();

            readIndexDto.ReadXml(reader);

            Assert.AreEqual(readIndexDto.Indexes.Count(), 2);
        }


        /// <summary>
        ///   Reading permissions from xml.
        /// </summary>
        [TestMethod]
        public void ReadingPermissionsFromXml()
        {
            // Creating the table
            var xml = string.Format("<{0} {1}=\"{2}\">",
                TableDto.XmlTag,
                TableDto.NameAttribute,
                StringProperty.RandomIdentifier());

            // Creating the permissions list
            xml += string.Format("<{0}><{1} {2}=\"{3}\" {4}=\"{5}\" {6}=\"{7}\" /><{8} {9}=\"{10}\" {11}=\"{12}\" {13}=\"{14}\"></{15}></{16}></{17}>",
                TableDto.PermissionListTag,
                TablePermissionDto.XmlTag,
                TablePermissionDto.PrincipalNameAttribute,
                "principal_name",
                TablePermissionDto.DataActionAttribute,
                (int)DataAction.Select,
                TablePermissionDto.AccessLevelAttribute,
                (int)AccessLevel.ChildUnits,
                TablePermissionDto.XmlTag,
                TablePermissionDto.PrincipalNameAttribute,
                "principal",
                TablePermissionDto.DataActionAttribute,
                (int)DataAction.Update,
                TablePermissionDto.AccessLevelAttribute,
                (int)AccessLevel.ChildAndParentUnits,
                TablePermissionDto.XmlTag,
                TableDto.PermissionListTag,
                TableDto.XmlTag);

            var stream = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

            var tableDto = new TableDto();

            tableDto.ReadXml(stream);

            Assert.AreEqual(tableDto.Permissions.Count(), 2);
        }


        /// <summary>
        ///   Reading an empty permission list from xml.
        /// </summary>
        [TestMethod]
        public void ReadingEmptyPermissionListFromXml()
        {
            var xml = string.Format("<{0} {1}=\"{2}\">",
                TableDto.XmlTag,
                TableDto.NameAttribute,
                "my_table");

            // Creating an empty permission list
            xml += string.Format("<{0} /></{1}>",
                TableDto.PermissionListTag,
                TableDto.XmlTag);

            var stream = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

            var refColumnDto = new TableDto();

            refColumnDto.ReadXml(stream);

            Assert.AreEqual(refColumnDto.Permissions.Count(), 0);
        }


        /// <summary>
        ///   Writing and reading permission list.
        /// </summary>
        [TestMethod]
        public void WritingAndReadingPermissionList()
        {
            var tableDto = new TableDto
            {
                Name = "my_table"
            };

            tableDto.AddPermission(new TablePermissionDto
            {
                PrincipalName = "my_principal",
                DataAction = DataAction.Insert,
                AccessLevel = AccessLevel.ChildAndParentUnits
            });

            tableDto.AddPermission(new TablePermissionDto
            {
                PrincipalName = "my_principal",
                DataAction = DataAction.Delete,
                AccessLevel = AccessLevel.Unit
            });

            var memoryStream = new MemoryStream();
            var writer = new XmlTextWriter(memoryStream, Encoding.UTF8);

            tableDto.WriteXml(writer);

            writer.Flush();

            memoryStream.Position = 0;
            var streamReader = new StreamReader(memoryStream);
            var xml = streamReader.ReadToEnd();

            Assert.IsFalse(string.IsNullOrEmpty(xml));

            memoryStream.Position = 0;
            var reader = new XmlTextReader(memoryStream);

            var readIndexDto = new TableDto();

            readIndexDto.ReadXml(reader);

            Assert.AreEqual(readIndexDto.Permissions.Count(), 2);
        }
    }
}
